package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.constant.HttpStatus;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.bean.BeanUtils;
import com.zzyl.framework.config.properties.HuaWeiIotConfigProperties;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import com.zzyl.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * deviceService业务层处理
 * 
 * @author ruoyi
 * @date 2025-10-23
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 查询device
     * 
     * @param id device主键
     * @return device
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询device列表
     * 
     * @param device device
     * @return device
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增device
     * 
     * @param device device
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return save(device) ? 1 : 0;
    }

    /**
     * 修改device
     * 
     * @param dto device
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateDevice(DeviceDto dto)
    {
        Long count = this.lambdaQuery().eq(Device::getBindingLocation, dto.getBindingLocation()).count();
        if (count > 0) {
            throw new BaseException("不能在同一个地方绑定该设备");
        }
        UpdateDeviceRequest request  = new UpdateDeviceRequest();
        UpdateDevice body = new UpdateDevice();
        body.setDeviceName(dto.getNickname());
        body.setDescription(dto.getNickname());
        request.setDeviceId(dto.iotId);
        request.setBody(body);
        ioTDAClient.updateDevice(request);
        Device bean = BeanUtil.toBean(dto, Device.class);
        dto.setProductName(dto.getNickname());
        return updateById(bean) ? 1 : 0;
    }


    /**
     * 删除device信息
     * 
     * @param dto device主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteDeviceById(DeviceDto dto)
    {
        Device device = this.lambdaQuery().eq(Device::getIotId, dto.getIotId()).eq(Device::getProductKey,dto.getProductKey()).one();
        if (device == null) {
            throw new BaseException("找不到该设备");
        }
        DeleteDeviceRequest request  = new DeleteDeviceRequest();
        request.setDeviceId(dto.getIotId());
        DeleteDeviceResponse deleteDeviceResponse = ioTDAClient.deleteDevice(request);
        if (deleteDeviceResponse.getHttpStatusCode()!=204){
            throw new BaseException("删除IOT设备失败");
        }
        return removeById(device.getId()) ? 1 : 0;
    }

    @Autowired
    private IoTDAClient ioTDAClient;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void syncProductList() {
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        ListProductsResponse listProductsResponse = ioTDAClient.listProducts(listProductsRequest);
        if (listProductsResponse.getHttpStatusCode() != 200) {
            throw new BaseException("物联网接口 - 查询产品，同步失败");
        }

        redisTemplate.opsForValue().set(CacheConstants.IOT_ALL_PRODUCT_LIST,JSONUtil.toJsonStr(listProductsResponse.getProducts()));
    }

    /**
     * 同步产品列表
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {
        String json = redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST).toString();
        if (StrUtil.isBlank(json)) {
            return Collections.emptyList();
        }

        List<ProductSummary> list = JSONUtil.toList(json, ProductSummary.class);
        List<ProductVo> collect = list.stream().map(x -> ProductVo.builder().productKey(x.getProductId()).productName(x.getName()).build()).collect(Collectors.toList());
        return collect;
    }

    @Override
    public void register(DeviceDto dto) {
        //检查设备名是否重复
        Long countByName = this.lambdaQuery().eq(Device::getDeviceName, dto.getDeviceName()).count();
        if (countByName > 0) {
            throw new  BaseException("设备名重复");
        }
        //前端没有传设备标识码，生成雪花算法值，当做唯一设备标识码
        String nodeId  = IdUtil.getSnowflakeNextIdStr();
        //设备备注名称（设备描述）
        String nickName = dto.getRegisterDeviceRequest().getNickname();


        //检查设备标识是否重复（这一步不查也行，因为设备标识码每次生成完全不一样）

        //检查当前位置是否绑定过相同产品
        Long countSame = this.lambdaQuery().eq(Device::getBindingLocation, dto.getBindingLocation()).eq(Device::getLocationType, dto.getLocationType()).count();
        if (countSame > 0) {
            throw new  BaseException("不要绑定同一个设备");
        }
        //注册设备到华为云IOT平台
        AddDeviceRequest addDeviceResponse = new AddDeviceRequest();
        AddDevice addBody = new AddDevice();
        addBody.setNodeId(nodeId);
        addBody.setDeviceName(nickName);
        addBody.setProductId(dto.getProductKey());
        addBody.setDescription(nickName);
        String secret = IdUtil.fastSimpleUUID();
        AuthInfo authInfo = new AuthInfo();
        authInfo.setSecret(secret);
        addBody.setAuthInfo(authInfo);

        addDeviceResponse.setBody(addBody);

        try {
            AddDeviceResponse resp = ioTDAClient.addDevice(addDeviceResponse);
            if (resp.getHttpStatusCode() != HttpStatus.CREATED) {
                throw new  BaseException("注册到IOT平台失败");
            }
            String iotId = resp.getDeviceId();
            Device device = BeanUtil.toBean(dto, Device.class);
            device.setIotId(iotId);
            device.setNodeId(nodeId);
            device.setSecret(secret);
            device.setNickname(nickName);
            device.setProductName(dto.getDeviceName() );
            device.setHaveEntranceGuard(nickName.contains("门禁")?1:0);

            String productId = dto.getProductKey();
            String productName = getProductNameFromCache(productId);
            if (StrUtil.isBlank(productName)) {
                device.setProductName(productName);
            }
            this.save(device);
        } catch (Exception e) {
            e.printStackTrace();
            throw new  BaseException("注册到IOT平台失败");
        }

        //雪花算法值当做唯一设备标识码

        //uuid当做唯一设备密钥

        //注意这个IOT的接口响应201表示创建成功，不是传统的200

        //保存设备到表中

    }

    private String getProductNameFromCache(String productId) {
        String json = redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST);
        if (StrUtil.isBlank(json)) {
            return null;
        }
        List<ProductSummary> list = JSONUtil.toList(json, ProductSummary.class);
        ProductSummary productSummary = list.stream().filter(x -> productId.equals(x.getProductId())).findFirst().get();
        if (productSummary != null) {
            return productSummary.getName();
        }
        return null;
    }

    @Autowired
    private ISysUserService userService;

    @Autowired
    private HuaWeiIotConfigProperties huaWeiIotConfigProperties;

    @Override
    public DeviceDetailVo queryDeviceDetail(DeviceDto dto) {
        Device device = this.lambdaQuery().eq(Device::getIotId, dto.getIotId()).eq(Device::getProductKey, dto.getProductKey()).one();
        if (device == null) {
            throw new BaseException("没有查询到设备");
        }
        //拷贝表中设备数据给VO
        DeviceDetailVo vo = new DeviceDetailVo();
        BeanUtils.copyProperties(device, vo);
        vo.setDeviceSecret(device.getSecret());
        vo.setGmtCreate(device.getCreateTime());
        vo.setCreator(userService.selectUserById(Long.valueOf(device.getCreateBy())).getUserName());
        vo.setRegion(huaWeiIotConfigProperties.getRegionId());

        ShowDeviceRequest request = new ShowDeviceRequest();
        request.setDeviceId(device.getIotId());
        try {
            ShowDeviceResponse response = ioTDAClient.showDevice(request);
            if (response==null) {
                throw new BaseException("调用华为IO接口查询设备详情失败");
            }
            BeanUtils.copyProperties(device, vo);
            String activeTime = response.getActiveTime();
            if (StrUtil.isNotBlank(activeTime)) {
                LocalDateTime localDateTime = OffsetDateTime.parse(activeTime).atZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
                vo.setGmtActive(localDateTime);
            }else {
                vo.setGmtActive(null);
            }
            if(StringUtils.isNotBlank(response.getFwVersion())) {
                //固件版本
                vo.setFirmwareVersion(response.getFwVersion());
            }
        } catch (Exception e) {
            throw new BaseException("调用华为IO接口查询设备详情失败");
        }

        return vo;
    }

    @Override
    public JSONObject queryProductModelInfo(DeviceDto deviceDto) {
        if (StrUtil.isBlank(deviceDto.getProductKey())) {
            throw new BaseException("请输入正确的参数");
        }
        String productKey = deviceDto.getProductKey();
        ShowProductRequest request  = new ShowProductRequest();
        request.setProductId(productKey);
        ShowProductResponse res = null;
        try {
            res = ioTDAClient.showProduct(request);
        } catch (Exception e) {
            throw new BaseException("查询产品详情失败");
        }
        List<ServiceCapability> serviceCapabilities = res.getServiceCapabilities();
        if (serviceCapabilities.isEmpty()){
            return null;
        }
        JSONObject jsonObject = JSONUtil.parseObj(serviceCapabilities.get(0));
        return jsonObject;
    }

    @Override
    public Map<String,Object> queryDevicePropertyStatus(String deviceName, String productKey) {

        Device device = this.lambdaQuery().eq(Device::getDeviceName, deviceName).eq(Device::getProductKey, productKey).one();
        if (device == null) {
            throw new BaseException("表中数据不存在");
        }
        ShowDeviceShadowRequest request  = new ShowDeviceShadowRequest();
        request.setDeviceId(device.getIotId());
        ShowDeviceShadowResponse showDeviceShadowResponse = ioTDAClient.showDeviceShadow(request);
        if (showDeviceShadowResponse.getHttpStatusCode()!=200) {
            throw new BaseException("物联网接口 - 查询设备上报数据，调用失败");
        }
        List<DeviceShadowData> shadow = showDeviceShadowResponse.getShadow();
        if(CollUtil.isEmpty(shadow)){
            List<Object> emptyList = Collections.emptyList();
            return AjaxResult.success(emptyList);
        }
        DeviceShadowData deviceShadowData = shadow.get(0);
        JSONObject properties = JSONUtil.parseObj(deviceShadowData.getReported().getProperties());

        LocalDateTime localDateTime = OffsetDateTime.parse(deviceShadowData.getReported().getEventTime(), DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX")).atZoneSameInstant(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
        List<Map<String,Object>> list = new ArrayList<>();
        properties.forEach((k,v)->{
            Map<String,Object> map = new HashMap<>();
            map.put("identifier",k);
            map.put("value",v);
            map.put("time",localDateTime);
            list.add(map);
        });
        Map<String,Object> propertyStatusInfo = new HashMap<>();
        propertyStatusInfo.put("propertyStatusInfo",list);
        Map<String,Object> map = new HashMap<>();
        map.put("list",propertyStatusInfo);
        return map;
    }
}
